const fs = require('fs')
const path = require('path')
const ChildProcess = require('child_process')
const packager = require('electron-packager')

const WIN = process.platform === 'win32'

module.exports = function(grunt) {
  var authCode
  try {
    authCode = grunt.file.readJSON('secret/auth_code.json')
  } catch (e) {
    if (e.origError.code === 'ENOENT') {
      console.warn(
        'secret/auth_code.json is not found. CodeSigning is not available.'
      )
    }
  }
  const OSX_COMMON_NAME = authCode != null ? authCode.OSX_COMMON_NAME : ''
  const WIN_CERT_PASSWORD = authCode != null ? authCode.WIN_CERT_PASSWORD : ''

  var initConfig = {
    pkg: grunt.file.readJSON('package.json'),
    'create-windows-installer': {
      x64: {
        appDirectory: path.join(__dirname, 'dist', 'Boostnote-win32-x64'),
        outputDirectory: path.join(__dirname, 'dist'),
        authors: 'MAISIN&CO., Inc.',
        exe: 'Boostnote.exe',
        loadingGif: path.join(__dirname, 'resources/boostnote-install.gif'),
        iconUrl: path.join(__dirname, 'resources/app.ico'),
        setupIcon: path.join(__dirname, 'resources/dmg.ico'),
        certificateFile: path.join(__dirname, 'secret', 'authenticode_cer.p12'),
        certificatePassword: WIN_CERT_PASSWORD,
        noMsi: true
      }
    },
    'electron-installer-debian': {
      app: {
        options: {
          name: 'boostnote',
          productName: 'Boostnote',
          genericName: 'Boostnote',
          productDescription: 'The opensource note app for developers.',
          arch: 'amd64',
          categories: ['Development', 'Utility'],
          icon: path.join(__dirname, 'resources/app.png'),
          bin: 'Boostnote'
        },
        src: path.join(__dirname, 'dist', 'Boostnote-linux-x64'),
        dest: path.join(__dirname, 'dist')
      }
    },
    'electron-installer-redhat': {
      app: {
        options: {
          name: 'boostnote',
          productName: 'Boostnote',
          genericName: 'Boostnote',
          productDescription: 'The opensource note app for developers.',
          arch: 'x86_64',
          categories: ['Development', 'Utility'],
          icon: path.join(__dirname, 'resources/app.png'),
          bin: 'Boostnote'
        },
        src: path.join(__dirname, 'dist', 'Boostnote-linux-x64'),
        dest: path.join(__dirname, 'dist')
      }
    }
  }

  grunt.initConfig(initConfig)
  grunt.loadNpmTasks('grunt-electron-installer')
  if (!WIN) {
    grunt.loadNpmTasks('grunt-electron-installer-debian')
    grunt.loadNpmTasks('grunt-electron-installer-redhat')
  }

  grunt.registerTask('compile', function() {
    var done = this.async()
    var execPath =
      path.join('node_modules', '.bin', 'webpack') +
      ' --config webpack-production.config.js'
    grunt.log.writeln(execPath)
    ChildProcess.exec(
      execPath,
      {
        env: Object.assign({}, process.env, {
          BABEL_ENV: 'production',
          NODE_ENV: 'production'
        })
      },
      function(err, stdout, stderr) {
        grunt.log.writeln(stdout)

        if (err) {
          grunt.log.writeln(err)
          grunt.log.writeln(stderr)
          done(false)
          return
        }
        done()
      }
    )
  })

  grunt.registerTask('pack', function(platform) {
    grunt.log.writeln(path.join(__dirname, 'dist'))
    var done = this.async()
    var opts = {
      name: 'Boostnote',
      arch: 'x64',
      dir: __dirname,
      version: grunt.config.get('pkg.config.electron-version'),
      'app-version': grunt.config.get('pkg.version'),
      'app-bundle-id': 'com.maisin.boost',
      asar: false,
      prune: true,
      overwrite: true,
      out: path.join(__dirname, 'dist'),
      ignore: /node_modules\/ace-builds\/(?!src-min)|node_modules\/ace-builds\/(?=src-min-noconflict)|node_modules\/devicon\/icons|^\/browser|^\/secret|\.babelrc|\.gitignore|^\/\.gitmodules|^\/gruntfile|^\/readme.md|^\/webpack|^\/appdmg\.json|^\/node_modules\/grunt/
    }
    switch (platform) {
      case 'win':
        Object.assign(opts, {
          platform: 'win32',
          icon: path.join(__dirname, 'resources/app.ico'),
          'version-string': {
            CompanyName: 'MAISIN&CO., Inc.',
            LegalCopyright: '© 2015 MAISIN&CO., Inc. All rights reserved.',
            FileDescription: 'Boostnote',
            OriginalFilename: 'Boostnote',
            FileVersion: grunt.config.get('pkg.version'),
            ProductVersion: grunt.config.get('pkg.version'),
            ProductName: 'Boostnote',
            InternalName: 'Boostnote'
          }
        })
        packager(opts, function(err, appPath) {
          if (err) {
            grunt.log.writeln(err)
            done(err)
            return
          }
          done()
        })
        break
      case 'osx':
        Object.assign(opts, {
          platform: 'darwin',
          darwinDarkModeSupport: true,
          icon: path.join(__dirname, 'resources/app.icns'),
          'app-category-type': 'public.app-category.developer-tools'
        })
        packager(opts, function(err, appPath) {
          if (err) {
            grunt.log.writeln(err)
            done(err)
            return
          }
          done()
        })
        break
      case 'linux':
        Object.assign(opts, {
          platform: 'linux',
          icon: path.join(__dirname, 'resources/app.icns'),
          'app-category-type': 'public.app-category.developer-tools'
        })
        packager(opts, function(err, appPath) {
          if (err) {
            grunt.log.writeln(err)
            done(err)
            return
          }
          done()
        })
        break
    }
  })

  grunt.registerTask('codesign', function(platform) {
    var done = this.async()
    if (process.platform !== 'darwin') {
      done(false)
      return
    }

    ChildProcess.exec(
      `codesign --verbose --deep --force --timestamp=none --sign \"${OSX_COMMON_NAME}\" dist/Boostnote-darwin-x64/Boostnote.app`,
      function(err, stdout, stderr) {
        grunt.log.writeln(stdout)
        if (err) {
          grunt.log.writeln(err)
          grunt.log.writeln(stderr)
          done(false)
          return
        }
        done()
      }
    )
  })

  grunt.registerTask('create-osx-installer', function() {
    var done = this.async()
    var execPath = 'appdmg appdmg.json dist/Boostnote-mac.dmg'
    grunt.log.writeln(execPath)
    ChildProcess.exec(execPath, function(err, stdout, stderr) {
      grunt.log.writeln(stdout)
      if (err) {
        grunt.log.writeln(err)
        grunt.log.writeln(stderr)
        done(false)
        return
      }
      done()
    })
  })

  grunt.registerTask('zip', function(platform) {
    var done = this.async()
    switch (platform) {
      case 'osx':
        var execPath =
          'cd dist/Boostnote-darwin-x64 && zip -r -y -q ../Boostnote-mac.zip Boostnote.app'
        grunt.log.writeln(execPath)
        ChildProcess.exec(execPath, function(err, stdout, stderr) {
          grunt.log.writeln(stdout)
          if (err) {
            grunt.log.writeln(err)
            grunt.log.writeln(stderr)
            done(false)
            return
          }
          done()
        })
        break
      default:
        done()
        return
    }
  })

  function getTarget() {
    switch (process.platform) {
      case 'darwin':
        return 'osx'
      case 'win32':
        return 'win'
      case 'linux':
        return 'linux'
      default:
        return process.platform
    }
  }

  grunt.registerTask('build', function(platform) {
    if (platform == null) platform = getTarget()

    switch (platform) {
      case 'win':
        grunt.task.run(['compile', 'pack:win', 'create-windows-installer'])
        break
      case 'osx':
        grunt.task.run([
          'compile',
          'pack:osx',
          'codesign',
          'create-osx-installer',
          'zip:osx'
        ])
        break
      case 'linux':
        grunt.task.run([
          'compile',
          'pack:linux',
          'electron-installer-debian',
          'electron-installer-redhat'
        ])
        break
    }
  })

  grunt.registerTask('pre-build', function(platform) {
    if (platform == null) platform = getTarget()

    switch (platform) {
      case 'win':
        grunt.task.run(['compile', 'pack:win'])
        break
      case 'osx':
        grunt.task.run(['compile', 'pack:osx'])
        break
      case 'linux':
        grunt.task.run(['compile', 'pack:linux'])
    }
  })

  grunt.registerTask('bfm', function() {
    const Color = require('color')
    const parseCSS = require('css').parse

    function generateRule(selector, bgColor, fgColor) {
      if (bgColor.isLight()) {
        bgColor = bgColor.mix(fgColor, 0.05)
      } else {
        bgColor = bgColor.mix(fgColor, 0.1)
      }

      if (selector && selector.length > 0) {
        return `${selector} .cm-table-row-even { background-color: ${bgColor
          .rgb()
          .string()}; }`
      } else {
        return `.cm-table-row-even { background-color: ${bgColor
          .rgb()
          .string()}; }`
      }
    }

    const root = path.join(__dirname, 'node_modules/codemirror/theme/')

    const colors = fs
      .readdirSync(root)
      .filter(file => file !== 'solarized.css')
      .map(file => {
        const css = parseCSS(fs.readFileSync(path.join(root, file), 'utf8'))

        const rules = css.stylesheet.rules.filter(
          rule => rule.selectors && /\b\.CodeMirror$/.test(rule.selectors[0])
        )
        if (rules.length === 1) {
          let bgColor = Color('white')
          let fgColor = Color('black')

          rules[0].declarations.forEach(declaration => {
            if (
              declaration.property === 'background-color' ||
              declaration.property === 'background'
            ) {
              bgColor = Color(declaration.value.split(' ')[0])
            } else if (declaration.property === 'color') {
              const value = /^(.*?)(?:\s*!important)?$/.exec(
                declaration.value
              )[1]
              const match = /^rgba\((.*?),\s*1\)$/.exec(value)
              if (match) {
                fgColor = Color(`rgb(${match[1]})`)
              } else {
                fgColor = Color(value)
              }
            }
          })

          return generateRule(rules[0].selectors[0], bgColor, fgColor)
        }
      })
      .filter(value => !!value)

    // default
    colors.unshift(generateRule(null, Color('white'), Color('black')))
    // solarized dark
    colors.push(
      generateRule(
        '.cm-s-solarized.cm-s-dark',
        Color('#002b36'),
        Color('#839496')
      )
    )
    // solarized light
    colors.push(
      generateRule(
        '.cm-s-solarized.cm-s-light',
        Color('#fdf6e3'),
        Color('#657b83')
      )
    )

    fs.writeFileSync(
      path.join(__dirname, 'extra_scripts/codemirror/mode/bfm/bfm.css'),
      colors.join('\n'),
      'utf8'
    )
  })

  grunt.registerTask('default', ['build'])
}
